// 数据响应式

// 数组的响应式
// 找到原型
const originalProto = Array.prototype;
// 对这个原型做个备份，在备份上做操作
const arrayProto = Object.create(originalProto);
['push', 'pop', 'shift', 'unshift', 'splice', 'reverse', 'sort'].forEach((methodName) => {
  arrayProto[methodName] = function () {
    // 原始操作
    originalProto[methodName].apply(this, arguments);
    // 其他操作  通知更新
    console.log('数组变化了', methodName, arguments);
  };
});

// 对象的 key设置成响应式 默认值是 val
function defineReactive(obj, key, val) {
  // 递归处理
  observe(val);

  Object.defineProperty(obj, key, {
    get() {
      console.log('get', key);
      return val;
    },
    set(newVal) {
      if (newVal !== val) {
        console.log('set', key, newVal);
        observe(newVal);
        val = newVal;
      }
    },
  });
}

// 让我们使一个对象所有属性都被拦截
function observe(obj) {
  if (typeof obj !== 'object' || obj == null) {
    return;
  }

  // 判断传入数据的类型
  if (Array.isArray(obj)) {
    // 覆盖原型
    obj.__proto__ = arrayProto;
    // 将数组内部元素响应化
    for (let i = 0; i < obj.length; i++) {
      observe(obj[i]);
    }
  } else {
    Object.keys(obj).forEach((key) => {
      defineReactive(obj, key, obj[key]);
    });
  }
}

// 由于新增属性无法被拦截，所以必须有特定api做对应响应式拦截
function set(obj, key, val) {
  defineReactive(obj, key, val);
}

const obj = { foo: 'foo', bar: 'bar', baz: { a: 1 }, arr: [1, 2, 3] };
// defineReactive(obj, 'foo', 'foo')
observe(obj);
// obj.foo
// obj.foo = 'fooooooooooo'

// obj.bar
// obj.baz.a
// obj.baz = {a: 100}
// obj.baz.a

// obj.dong = 'dong'
set(obj, 'dong', 'dong');
obj.dong;

obj.arr.push(4);

// 思考题：数组响应式是怎么做的
// 找到数组的原型  将能修改数组的方法做一个增强，在这些方法内部添加一个通知更新的操作

// 数组原型中有7个可以改变数组的方法，把它们覆盖为可以做拦截通知的方法
